package com.airport.schedule.greedy;

import com.airport.schedule.Problem;
import com.airport.schedule.ScheduleSolution;
import com.airport.schedule.model.Flight;
import com.airport.schedule.model.FlightGate;
import com.airport.schedule.util.ListUtil;
import com.airport.schedule.util.MapUtil;
import com.airport.schedule.util.SetUtil;
import edu.stanford.nlp.util.IntervalTree;

import java.util.*;

public class GreedyNormalScheduler extends Thread {

    private Problem problem;
    private Map<Flight, List<FlightGate>> flightAvailableGates;
    private Map<Flight, Double> flight2ConflictRate;
    private List<Flight> unassignedFlights;
    private Random random = new Random(System.currentTimeMillis());

    private ScheduleSolution solution;

    private double gamma;
    private double alpha = 0.01;

    public GreedyNormalScheduler(Problem problem, double gamma, boolean useAdHocGate) {
        this.problem = problem;
        this.flightAvailableGates = problem.getFlightAvailableGates(useAdHocGate);
        this.unassignedFlights = new ArrayList<>();
        this.flight2ConflictRate = new HashMap<>();
        this.gamma = gamma;
//        IntervalTree<Integer, Flight> intervalTree = new IntervalTree<>();
        List<Flight> flights = problem.getScheduleFlights();
//        intervalTree.addAll(flights);
        flights.forEach(iFlight->{
            Set<FlightGate> iGates = new HashSet<>(flightAvailableGates.get(iFlight));
//            List<Flight> conflictFlights = intervalTree.getOverlapping(iFlight);
//            conflictFlights.remove(iFlight);
            flight2ConflictRate.put(iFlight, 0.0);
            flights.forEach(jFlight->{
                Set<FlightGate> jGates = new HashSet<>(flightAvailableGates.get(jFlight));
                Set<FlightGate> common = SetUtil.intersect(iGates, jGates);
                flight2ConflictRate.put(iFlight, flight2ConflictRate.get(iFlight)
                        + (common.size()+0.0)/jGates.size());
            });
        });
        System.out.println("Greedy Scheduler init successfully!");
    }

    public List<Flight> getUnassignedFlights() {
        return unassignedFlights;
    }

    public ScheduleSolution getSolution() {
        return solution;
    }

    @Override
    public void run() {
        this.solution = greedyProcess();
    }

    public ScheduleSolution greedyProcess() {
        ScheduleSolution solution = new ScheduleSolution(problem);
        // 贪心策略一：按照冲突影响率排序，冲突率小的航班先得到机位, 冲突率一样的按照机位多的先获得
        List<Flight> flights = new ArrayList<>(problem.getScheduleFlights());
        Collections.sort(flights, (f1, f2)-> {
            int r = Double.compare(flight2ConflictRate.get(f1), flight2ConflictRate.get(f2));
            if(r == 0) {
                int peopleNum1 = f1.getNumPeople();
                int peopleNum2 = f2.getNumPeople();
                if(peopleNum1 == peopleNum2) {
                    int i1 = flightAvailableGates.get(f1).size();
                    int i2 = flightAvailableGates.get(f2).size();
                    return i2 - i1;
                }
                return peopleNum2 - peopleNum1;
            }
            return r;
        });
        // 开始分配，按照先来先得的贪心策略
        while(!flights.isEmpty()) {
            Flight flight = flights.remove(0);
            List<FlightGate> unoccupiedNormalGates = solution.getUnoccupiedNormalFlightGates(flight, false);
            if(!unoccupiedNormalGates.isEmpty()) {// 贪心策略二：先优先正常机位
                if(unoccupiedNormalGates.size() == 1) { // 只有一个正常机位 看情况抢占
                    solution.remove(flight, unoccupiedNormalGates.get(0));
                }else{
                    FlightGate gate = findBestFlightGate(unoccupiedNormalGates, solution, flight, flights);
                    solution.assign(flight, gate);
//                    List<FlightGate> nearGates = ListUtil.filter(unoccupiedNormalGates, FlightGate::isNearGate);
//                    if(nearGates.size() == 1) { // 只有一个正常近机位 抢占！
//                        solution.assign(flight, nearGates.get(0));
//                    }else{ // 近机位数不是1，做抉择的时候到了！
//                        // 优先尝试近机位
//                        List<FlightGate> candidates = nearGates.size() > 1 ? nearGates : unoccupiedNormalGates;
//                        if(!flights.isEmpty()) {
//                            FlightGate gate = findBestFlightGate(candidates, solution, flight, flights);
//                            solution.assign(flight, gate);
//                        }else{ // 最后一只飞机, 直接寻找滑道冲突率最小的机位
//                            int minRunwayConflicts = Integer.MAX_VALUE;
//                            FlightGate bestGate = null;
//                            for(FlightGate gate : unoccupiedNormalGates) {
//                                solution.assign(flight, gate);
//                                int num = solution.getRunwayConflictNum();
//                                if(num < minRunwayConflicts) {
//                                    minRunwayConflicts = num;
//                                    bestGate = gate;
//                                }
//                                solution.remove(flight, gate);
//                            }
//                            solution.assign(flight, bestGate);
//                        }
//                    }
                }
            }else{ // 没有正常机位，除非采取临时措施！
                unassignedFlights.add(flight);
            }
        }
        return solution;
    }

    private boolean canAssignCurrentGate(Flight flight, FlightGate currentGate,
                                         List<Flight> flights, ScheduleSolution solution) {
        int currentNoFlightGateNum = getNoFlightGateNum(solution, flights);
        solution.assign(flight, currentGate);
        int noFlightGateNum = getNoFlightGateNum(solution, flights);
        return noFlightGateNum - currentNoFlightGateNum <= 1;
    }

    private int getNoFlightGateNum(ScheduleSolution sol, List<Flight> restFlight) {
        int num = 0;
        for(Flight flight : restFlight) {
            if(sol.getAvailableAdHocFlightGates(flight).isEmpty()
                    && sol.getUnoccupiedNormalFlightGates(flight, false).isEmpty()) {
                num++;
            }
        }
        return num;
    }

    private FlightGate findBestFlightGate(List<FlightGate> candidates, ScheduleSolution solution,
                                          Flight curFlight, List<Flight> unassignedFlights) {
        Map<FlightGate, Double> gateOccupiedRate = new HashMap<>();
        Map<FlightGate, Double> gateRunwayConflictRate = new HashMap<>();
        for(FlightGate g : candidates) {
            gateOccupiedRate.put(g, 0.0);
            gateRunwayConflictRate.put(g, 0.0);
        }
        Set<FlightGate> curFlightGates = new HashSet<>(candidates);
        for(Flight comingFlight : unassignedFlights) {
            Set<FlightGate> comingFlightGates =
                    new HashSet<>(solution.getUnoccupiedNormalFlightGates(comingFlight, false));
            if(SetUtil.hasIntersect(curFlightGates, comingFlightGates)) {
                for(FlightGate gate : curFlightGates) {
                    if(comingFlightGates.contains(gate)) {
                        gateOccupiedRate.put(gate, gateOccupiedRate.get(gate)+1.0/comingFlightGates.size());
                    }
                }
            }
        }
        for(FlightGate gate : candidates) {
            solution.assign(curFlight, gate);
            int conflictNum = solution.getRunwayConflictNum();
            solution.remove(curFlight, gate);
            gateRunwayConflictRate.put(gate, gateRunwayConflictRate.get(gate)+conflictNum);
        }
        MapUtil.normalize(gateOccupiedRate);
        MapUtil.normalize(gateRunwayConflictRate);
        double minVal = Double.MAX_VALUE;
        List<FlightGate> bestGates = new ArrayList<>();
        for(Map.Entry<FlightGate, Double> entry : gateOccupiedRate.entrySet()) {
            double value = (1 - gamma) * entry.getValue() + gamma * gateRunwayConflictRate.get(entry.getKey());
            if(entry.getKey().isNearGate()) {
                value -= alpha;
            }
            if(value == minVal) {
                bestGates.add(entry.getKey());
            }else if(value < minVal) {
                bestGates = new ArrayList<>();
                bestGates.add(entry.getKey());
                minVal = value;
            }
        }
        return bestGates.get(random.nextInt(bestGates.size()));
    }


}
